"""
Inserts Doxygen markup templates into new or existing source code.

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*  Copyright (C) 2004 by Daniel M. Gass                                     *
*                                                                           *
*  This program is free software; you can redistribute it and/or modify it  *
*  under the terms of version 2 of the GNU General Public License as        *
*  published by the Free Software Foundation.                               *
*                                                                           *
*  This program is distributed in the hope that it will be useful, but      *
*  WITHOUT ANY WARRANTY; without even the implied warranty of               *
*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General *
*  Public License for more details.                                         *
*                                                                           *
*  You should have received a copy of the GNU General Public License along  *
*  with this program; if not, write to the Free Software Foundation, Inc.,  *
*  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA                    *
*
*  This software is distributed as part of the testgen test framework.      *
*            https://sourceforge.net/projects/testgen/                      *
*                                                                           *
*  You may contact the author at <dan.gass at gmail.com>                    *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
"""
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# #
# #  Imports and Utility Function Definitions
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 

import glob
import fnmatch
import re
import sys

def get_files(args):
    """Expands argument (wildcards) and returns list of files"""
    filelist = []
    errors = []
    # each argument may be a wildcard - expand them
    for arg in args:
        names = glob.glob(arg)
        if not names:
            errors.append(arg)
        # go through files recording those that aren't generated by doxit
        for name in names:
            #@future [python22] use commented line when not supporting python2.2
            #if '.doxit.' not in name:
            if len(name.split('.doxit.')) == 1:
                filelist.append(name)
    # if any argument failed to find a match, write an error and prevent
    # script from processing any.
    if errors:
        sys.stderr.write("ERROR: '%s' not found.\n" % ' '.join(errors))
        filelist = []
    return filelist    

def expand_inserts(text):
    """Replaces special [[INSERT index HERE]] markers with contents of the
    insert dictionary (using index in pattern)."""
    regexp = re.compile('\[\[INSERT\s+([\[\]\.\w]+)\s+HERE\]\]')
    def replacer(matchobj):
        key = matchobj.group(1)
        return insert[key]
    while regexp.search(text) is not None:
        text = regexp.sub(replacer,text)
    return text

template_prefix = """
#error - Update following Doxygen template
"""

def make_template(template):
    """Returns a template with standard text on the front of it"""
    return template_prefix + template.lstrip()

def make_file_header(filename):
    text = file_header.replace('[[file name]]',filename)
    for  pattern,  brief_description in [
        ('*def.h', 'Contains defines for the [ss] subsystem.'),
        ('*ext.h', 'Contains variable declarations/externs for the [ss] subsystem.'),
        ('*fnc.h', 'Contains function prototypes for the [ss] subsystem.'),
        ('*inc.h', 'Contains header file includes for the [ss] subsystem.'),
        ('*str.h', 'Contains structures, enumerations, and typedefs for the [ss] subsystem.'),
        ('*.c',    'Contains [ss] subsystem functions associated with [Description].'),
        ('*.hpp',  'Contains [class] object implementations.'),
        ('*.cpp',  'Contains [class] object implementations.\n///\n/// @see [Ss][Class].hpp for a detailed description of this class.'),
        ('*',      '[brief file description]'),
        ]:
        if fnmatch.fnmatch(filename, pattern):
            return text.replace('[brief file description]',brief_description)
            break

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# #
# #  Templates
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 

insert, template = {}, {}

file_header = """
/////////////////////////////////////////////////////////////////////////////
/// @file [[file name]]
///
/// [brief file description]
///
/// @par Edit History
/// - [0]   [[username]] [[day]]-[[month]]-[[year]] Initial revision of file.
///
/// @par Copyright &copy; [[year]] Your Company, Inc. All rights reserved.
///
/// @ingroup [ss]
/////////////////////////////////////////////////////////////////////////////
""".lstrip()

insert['_standard_stuff_'] = """
/////////////////////////////////////////////////////////////////////////////
/// [Add detailed description, remove if not needed.  Note, the brief
///  description will appear with the detailed description.]
///
/// @note [Add note text if necessary, otherwise remove this line.]
/////////////////////////////////////////////////////////////////////////////
""".strip('/ \n\t')

template['class'] = make_template("""
/////////////////////////////////////////////////////////////////////////////
//  CLASS NAME: [class name]
//
/// [Brief one sentence class description]
///
/// [[INSERT _standard_stuff_ HERE]]
/////////////////////////////////////////////////////////////////////////////
""")

template['define'] = make_template("""
/////////////////////////////////////////////////////////////////////////////
/// [Brief one sentence define description]
///
/// [[INSERT _standard_stuff_ HERE]]
/////////////////////////////////////////////////////////////////////////////
""")

template['enum'] = make_template("""
/////////////////////////////////////////////////////////////////////////////
/// [Brief one sentence enumeration description]
///
/// [[INSERT _standard_stuff_ HERE]]
/////////////////////////////////////////////////////////////////////////////
""")

template['struct'] = make_template("""
/////////////////////////////////////////////////////////////////////////////
/// [Brief one sentence structure description]
///
/// [[INSERT _standard_stuff_ HERE]]
/////////////////////////////////////////////////////////////////////////////
""")

template['typedef'] = make_template("""
/////////////////////////////////////////////////////////////////////////////
/// [Brief one sentence typedef description]
///
/// [[INSERT _standard_stuff_ HERE]]
/////////////////////////////////////////////////////////////////////////////
""")

template['function'] = make_template("""
/////////////////////////////////////////////////////////////////////////////
//  [[function name]]
//
/// [Add brief one sentence description.]
///
/// [Add detailed description, remove if not needed.  Note, the brief
///  description will appear with the detailed description.]
///
/// @note [Add note text if necessary, otherwise remove this line.]
///
/// [[@param]] [Add parameter description (remove line when no params).]
///
/// [[@return]] [Add description of what is returned (remove line when nothing returned).]
/// @retval [Add description of a specific return value (remove line when nothing returned).]
/////////////////////////////////////////////////////////////////////////////
""")

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# #
# #  Command Line Interface
# #
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # 

def command_line(args=None):
    """Performs processing associated with the command line use of the
    doxit module.  Present so other modules can use this functionality
    by importing this function rather than a system call."""

    #@future [python22] remove try structure when not supporting 22    
    try:
        import optparse
    except ImportError:
        import testgen.optik as optparse
    import os
    import testgen.c.doxygen
    import time

    # Get file list and options from command line arguments
    parser = optparse.OptionParser(usage='%prog filepattern1 [filepattern2] ...')
    options, args = parser.parse_args(args)
    filelist = get_files(args)

    for filename in filelist:

        #
        # FUNCTIONS
        #
        # Use unit test stubber derivative to insert templates above functions/methods
        src = testgen.c.doxygen.TemplateHelper(filename)
        src.insert_function_headers(template['function'])
        text = src.get_filecode()
        # Remove @param, @return, @retval tags if no params or return value
        text = re.sub(r'(///[ \t]*\n)*/// @return void .*\n(/// @retval .*\n)*','',text)
        text = re.sub(r'(///[ \t]*\n)*/// @param\[in,out\] None .*\n','',text)

        #
        # CLASS, #DEFINE, ENUM, STRUCT, TYPEDEF
        #
        # Insert Doxygen markup above other c/c++ code constructs
        def insert_template(text,template,regexp):
            def replacer(matchobj):
                return template + matchobj.group(0).lstrip()
            return regexp.sub(replacer,text)
        text = insert_template(text, template['class'],   re.compile('\nclass '))
        text = insert_template(text, template['define'],  re.compile('\n#define '))
        text = insert_template(text, template['enum'],    re.compile('\nenum '))
        text = insert_template(text, template['struct'],  re.compile('\nstruct '))
        text = insert_template(text, template['typedef'], re.compile('\ntypedef '))

        #
        # DON'T ADD TO OLD DOXYGEN MARKUP
        #
        # Code items that had existing doxygen markup now have two, get rid of new one
        def replacer(matchobj):
            return matchobj.group('keep')
        regexp = re.compile(r'(?P<keep>////+\s*\n)(////+\s*\n)*#error.*\n////+\s*\n(///\s.*\n)+////+\s*\n')
        text = regexp.sub(replacer,text)

        #
        # FILE HEADER
        #
        # Insert a customized file header (controlled by filename), but only do it if no existing
        # Doxygen file header is present.
        #@future [python22] use following line when not supporting python22
        #if '@file' not in text:
        if not re.findall('@file',text):
            text = make_template(make_file_header(filename)).lstrip() + text

        #
        # MISC SUBSTITUTIONS (file name, date, user name)
        #
        # Do misc text substitutions
        year,month,day = time.localtime()[0:3]
        month = '0 JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC'.split()[month]
        username = os.getenv('USERNAME','[user name]')
        initials = username.upper()[0:3]
        text = text.replace('[[file name]]',filename)
        text = text.replace('[[year]]',str(year))
        text = text.replace('[[month]]',month)
        text = text.replace('[[day]]','%02d' % day)
        text = text.replace('[[username]]',username)
        text = text.replace('[[initials]]',initials)

        #
        # CREATE THE NEW FILE (with 'doxit' extension)
        #
        newname = "%s.doxit%s" % os.path.splitext(filename)
        print newname
        f = open(newname,'w')
        f.write(expand_inserts(text))
        f.close()

if __name__ == "__main__":

    command_line()
   
